/**
 * 
 */
package com.huawei.virest.domain;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.huawei.virest.common.SystemInit;
import com.huawei.virest.common.constant.Constants;
import com.huawei.virest.common.domain.HttpResp;
import com.huawei.virest.common.domain.MatchedResult;
import com.huawei.virest.common.domain.VIRestException;
import com.huawei.virest.common.util.Utils;

/**
 * @author 025079
 *
 */
public class StructuredData
{
    
    /**
     * 请求文件Map
     */
    private Map<String, File> rcqMap = new TreeMap<String, File>();
    
    /**
     * 响应文件Map
     */
    private Map<String, File> rcsMap = new HashMap<String, File>();
    
    /**
     * 用例列表
     */
    private Map<String, Map<String, Object>> caseList;
    
    /**
     * 成功数
     */
    private int SUCCESS_COUNT = 0;
    
    /**
     * 结果Map
     */
    private Map<String, MatchedResult> resultMap = new TreeMap<String, MatchedResult>();
    
    public StructuredData()
    {
        
    }
    
    public Map<String, File> getRcqMap()
    {
        return rcqMap;
    }
    
    public void setRcqMap(Map<String, File> rcqMap)
    {
        this.rcqMap = rcqMap;
    }
    
    public Map<String, File> getRcsMap()
    {
        return rcsMap;
    }
    
    public void setRcsMap(Map<String, File> rcsMap)
    {
        this.rcsMap = rcsMap;
    }
    
    public Map<String, Map<String, Object>> getCaseList()
    {
        return caseList;
    }
    
    public void setCaseList(Map<String, Map<String, Object>> caseList)
    {
        this.caseList = caseList;
    }
    
    public Map<String, MatchedResult> getResultMap()
    {
        return resultMap;
    }
    
    public void setResultMap(Map<String, MatchedResult> resultMap)
    {
        this.resultMap = resultMap;
    }
    
    /**
     * 读取结构化数据
     * @param file 文件
     */
    public void readStructuredData(File file)
    {
        
        if (file.isDirectory())
        {
            // 只读取文件夹里以.rcq或.rcs的后缀的文件
            //            String[] suffixes = new String[] {REQUEST_SUFFIX, RESPONSE_SUFFIX};
            //            FilenameFilter filter = new SuffixFileFilter(suffixes);
            
            FilenameFilter filter = new FilenameFilter()
            {
                
                public boolean accept(File dir, String name)
                {
                    if (dir.isDirectory())
                    {
                        return true;
                    }
                    else if (dir.isFile()
                        && (name.endsWith(SystemInit.getConfig().getRequestSuffix()) 
                        || name.endsWith(SystemInit.getConfig().getResponseSuffix())))
                    {
                        return true;
                    }
                    return false;
                }
            };
            
            File[] files = file.listFiles(filter); // 过滤	
            for (int i = 0; i < files.length; i++)
            {
                readStructuredData(files[i]);
            }
        }
        else
        {
            // 非目录
            if (file.getName().endsWith(SystemInit.getConfig().getRequestSuffix()))
            {
                rcqMap.put(Utils.getFilenameWithoutExtension(file.getName()),
                        file);
            }
            else if (file.getName().endsWith(SystemInit.getConfig().getResponseSuffix()))
            {
                rcsMap.put(Utils.getFilenameWithoutExtension(file.getName()),
                        file);
            }
        }
    }
    
    /**
     * 解析请求数据
     * @param file 请求数据文件
     * @return 结构化请求数据
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> parseRequestData(File file)
    {
        Map<String, Object> requestMap = new HashMap<String, Object>();
        
        try
        {
            Document document = Utils.parseXML(file);// 解析XML文件
            Map<String, String> headerMap = null;
            
            Element rootElement = document.getRootElement();
            List elements = rootElement.getChild(SystemInit.getConfig().getRequestRootElement()).getChildren();
            for (Iterator<Element> iterator = elements.iterator(); iterator.hasNext();)
            {
                Element element = iterator.next();
                
                headerMap = new HashMap<String, String>();
                
                List<Element> children = element.getChildren();
                if (children != null && children.size() > 0)
                {
                    for (int i = 0; i < children.size(); i++)
                    {
                        Element child = children.get(i);
                        String key = child.getAttributeValue(Constants.HEADER_KEY);
                        String value = child.getAttributeValue(Constants.HEADER_VALUE);
                        headerMap.put(key, value);// 消息头处理
                    }
                    requestMap.put(element.getName(), headerMap);
                }
                else
                {
                    requestMap.put(element.getName(), element.getText());
                }
            }
        }
        catch (Exception e)
        {
            String key = Utils.getFilenameWithoutExtension(file.getName());
            resultMap.put(key, createResult(key, false, null, "请求XML文件编写错误: "
                    + e.getMessage()));
        }
        
        return requestMap;
    }
    
    /**
     * 解析响应数据
     * @param key 文件名
     * @param resp 请求响应
     */
    @SuppressWarnings("unchecked")
    public void parseResponseData(String key, HttpResp resp)
    {
        File file = rcsMap.get(key);
        
        // 如果调用脚本获取数据为空，则直接返回提示错误，该情况存在有：如url指向错误
        if (Utils.isNull(resp.getResponse()))
        {
            // 映射响应数据情况
            resp.setResponse("请求响应结果为空：" + resp.getHttpStatus() + "-"
                    + resp.getStatusText());
            resultMap.put(key, createResult(key, false, resp, "请求响应结果为空："
                    + resp.getStatusText() + "-" + resp.getStatusText()));
            return;
        }
        
        if (file == null)
        {
            // 映射文件不存在
            resultMap.put(key, createResult(key, false, resp, "响应结果匹配文件不存在"));
            return;
        }
        
        try
        {
            Document document = Utils.parseXML(file); // 解析XML文件
            Element rootElement = document.getRootElement();
            List elements = rootElement.getChild(SystemInit.getConfig().getResponseRootElement()).getChildren();
            
            // 返回结果是数组则做数据处理
            if (resp.getResponse().startsWith("[") && resp.getResponse().endsWith("]"))
            {
            	JSONArray jsonArr = resp.asJSONArray();
            	int len = jsonArr.length();
                for (int i = 0; i < len; i++)
                {
                	JSONObject jsonObj = jsonArr.getJSONObject(i);
                	matchingStructure(elements, jsonObj);// 匹配匹配
                }
            }
            else
            {
            	JSONObject jsonObj = resp.asJSONObject();            
            	// 结构匹配
            	matchingStructure(elements, jsonObj);
            }
            
            // 存入请求和响应脚本名称及结构比配结果
            resultMap.put(key, createResult(key, true, resp, "成功"));
            SUCCESS_COUNT++;
        }
        catch (JSONException e)
        {
            resultMap.put(key, createResult(key, false, resp, "JSON响应结果错误："
                    + e.getMessage()));
        }
        catch (DataConversionException e)
        {
            // XML 编写错误
            resultMap.put(key, createResult(key, false, resp, "响应结果匹配文件错误："
                    + e.getMessage()));
        }
        catch (VIRestException e)
        {
            resultMap.put(key, createResult(key, false, resp, "响应结果匹配文件错误："
                    + e.getMessage()));
        }
        catch (JDOMException e)
        {
            resultMap.put(key, createResult(key, false, resp, "请求XML文件编写错误: "
                    + e.getMessage()));
        }
        catch (IOException e)
        {
            resultMap.put(key, createResult(key, false, resp, "读取请求XML文件错误: "
                    + e.getMessage()));
        }
        catch (NullPointerException e) 
        {
        	resultMap.put(key, createResult(key, false, resp, "响应结果文件编写错误: 缺少属性参数，"
                    + e.getMessage()));
		}
        catch (RuntimeException e) {
        	resultMap.put(key, createResult(key, false, resp, "其他错误: "
                    + e.getMessage()));
		}
        catch (Exception e)
        {
        	resultMap.put(key, createResult(key, false, resp, "其他错误: "
                    + e.getMessage()));
        }
    }
    
    /**
     * 匹配结构
     * @param elements 元素
     * @param jsonObj json对象
     * @throws DataConversionException 数据转换异常
     * @throws JSONException JSON异常
     * @throws VIRestException VIRest异常
     * @throws IOException IOException
     */
    @SuppressWarnings("unchecked")
    private void matchingStructure(List elements, JSONObject jsonObj)
            throws DataConversionException, JSONException, VIRestException, IOException
    {
        
        for (Iterator<Element> iterator = elements.iterator(); iterator.hasNext();)
        {
            Element element = iterator.next();
            String name = element.getName();
            
            boolean isNull = getRequiredAttribute(element, "isNull").getBooleanValue();
            if (!isNull && (!jsonObj.has(name) || jsonObj.isNull(name)))
            {
                // 响应结果无必呈现参数
                throw new VIRestException("响应结果无必呈现参数：" + name);
            }
            else if (jsonObj.has(name) && !jsonObj.isNull(name))
            {
                boolean isArray = getRequiredAttribute(element, "isArray")
                        .getBooleanValue();
                boolean isObject = getRequiredAttribute(element, "isObject")
                        .getBooleanValue();
                if (isArray && isObject) // 数组对象
                {
                    JSONArray jsonArr = jsonObj.getJSONArray(name);
                    int len = jsonArr.length();

                    matchingCount(element, len, name);// 匹配数量
                    
                    List children = element.getChildren();
                    
                    for (int i = 0; i < len; i++)
                    {
                        JSONObject jsonObject = jsonArr.getJSONObject(i);
                        matchingStructure(children, jsonObject);
                    }
                }
                else if (isObject) // 对象
                {
                    List children = element.getChildren();
                    JSONObject jsonObject = jsonObj.getJSONObject(name);
                    matchingStructure(children, jsonObject);
                }
                else if (isArray) // 数组属性
                {
                    JSONArray jsonArr = jsonObj.getJSONArray(name);
                    int len = jsonArr.length();
                    
                    matchingCount(element, len, name);// 匹配数量
                    
                    for (int i = 0; i < len; i++)
                    {
                        Object object = jsonArr.get(i);
                        matchingAttribute(element, object, name);// 匹配属性
                    }
                }
                else
                // 属性
                {
                    Object object = jsonObj.get(name);
                    matchingAttribute(element, object, name);// 匹配属性
                }
            }
        }
    }

	/**
	 * @param element
	 * @return
	 * @throws VIRestException 
	 */
	private Attribute getRequiredAttribute(Element element, String attrName) throws IOException {
		Attribute attr = getAttribute(element, attrName);
		
		if (attr == null)
		{
			throw new IOException("请求参数必填属性["+attrName+"]丢失");
		}
		
		return attr;
	}
	
	private Attribute getAttribute(Element element, String attrName)
	{
		return element.getAttribute(attrName);
	}
    
    /**
     * 匹配数量
     * @param element 属性
     * @param len 长度
     * @param name 对象名
     * @throws DataConversionException DataConversionException
     * @throws VIRestException VIRestException
     */
    private void matchingCount(Element element, Integer len, String name) throws DataConversionException, VIRestException
    {
    	Attribute countAttr = getAttribute(element, "count");
        if (countAttr != null && len != null && countAttr.getIntValue() != len)
        {
        	// 响应数组长度不正确
        	throw new VIRestException(name + ": 响应数组长度不正确");
        }
    }
    
    /**
     * 匹配属性
     * @param element 属性
     * @param object 对象
     * @param name 对象名
     * @throws VIRestException VIRest异常
     */
    private void matchingAttribute(Element element, Object object, String name)
            throws VIRestException
    {
    	Attribute typeAttr = getAttribute(element, "type");
    	if (typeAttr != null)
    	{
    		String type = typeAttr.getValue();
        	matchingType(object, type, name); // 匹配类型
    	}
        
    	Attribute formatAttr = getAttribute(element, "format");
    	if (formatAttr != null)
    	{
        	String format = formatAttr.getValue();
            matchingFormat(object, format, name); // 匹配格式
    	}        
        
    	Attribute valuesAttr = getAttribute(element, "values");
    	if (valuesAttr != null)
    	{
    		String values = valuesAttr.getValue();
            matchingValues(object, values, name); // 匹配值
    	}        
        
    }
    
    /**
     * 匹配值
     * @param object 对象
     * @param values 值
     * @param name 对象名
     * @throws VIRestException VIRest异常
     */
    private void matchingValues(Object object, String values, String name)
            throws VIRestException
    {
        if (!Utils.isNull(values))
        {
            String[] vs = values.split(",");
            if (!binarySearch(vs, object.toString()))
            {
                // 参数值错误
                throw new VIRestException(name + ": 参数值错误");
            }
        }
    }
    
    private boolean binarySearch(String[] strs, String value)
    {
    	boolean flag = false;
    	for (int i = 0; i < strs.length; i++) {
			if (strs[i].equals(value))
			{
				flag = true;
				break;
			}
		}
    	return flag;
    }
    
    /**
     * 匹配格式
     * @param object 对象
     * @param format 格式
     * @param name 对象名
     * @throws VIRestException VIRest异常
     */
    private void matchingFormat(Object object, String format, String name)
            throws VIRestException
    {
        if (!Utils.isNull(format))
        {
            boolean flag = Pattern.matches(format, object.toString());
            if (!flag)
            {
                // 参数格式不正确
                throw new VIRestException(name + ": 参数格式不正确");
            }
        }
    }
    
    /**
     * 匹配类型
     * @param object 对象
     * @param type 类型
     * @param name 对象名
     * @throws VIRestException VIRest异常
     */
    private void matchingType(Object object, String type, String name)
            throws VIRestException
    {
        if (!Utils.isNull(type)
                && !(("integer".equals(type.toLowerCase()) || "int".equals(type.toLowerCase())) && Utils.isNumber(object.toString()))
                && !("string".equals(type.toLowerCase()) && object instanceof String)
                && !("boolean".equals(type.toLowerCase()) && object instanceof Boolean)
                && !("double".equals(type.toLowerCase()) && object instanceof Double)
                && !("float".equals(type.toLowerCase()) && object instanceof Float)
                && !("short".equals(type.toLowerCase()) && object instanceof Short)
                && !("long".equals(type.toLowerCase()) && object instanceof Long)
                && !(("char".equals(type.toLowerCase()) || "character".equals(type.toLowerCase())) && object instanceof Character))
        {
            // 参数类型不正确
            throw new VIRestException(name + ": 参数类型不正确");
        }
    }
    
    /**
     * 创建结果
     * @param key 文件名
     * @param flag 成功或失败
     * @param resp 
     * @param msg 消息
     * @return 结果
     */
    private MatchedResult createResult(String key, boolean flag, HttpResp resp,
            String msg)
    {
        MatchedResult result = new MatchedResult();
        
        result.setFlag(String.valueOf(flag));
        
        result.setMsg(msg);
        
        if (resp == null)
        {
            resp = new HttpResp(null);// 处理请求文件编写错误问题
            resp.setResponse(msg);
        }
        result.setResponse(resp.getResponse());
        
        String scriptPath = rcqMap.get(key).getPath() + "<br>";
        if (rcsMap.get(key) != null)
        {
            scriptPath += rcsMap.get(key).getPath();
        }
        result.setScriptPath(scriptPath);
        
        return result;
    }
    
    /**
     * 获取成功数
     * @return 成功数
     */
    public int getSuccessCount()
    {
        return SUCCESS_COUNT;
    }
    
    /**
     * 获取总数
     * @return 总数
     */
    public int getTotalCount()
    {
        return rcqMap.size();
    }
    
}
